package com.awg.studyplan.excel.importtemplate;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.List;
import org.apache.commons.io.FileUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.log4j.Logger;
import org.apache.poi.hssf.usermodel.DVConstraint;
import org.apache.poi.hssf.usermodel.HSSFCell;
import org.apache.poi.hssf.usermodel.HSSFCellStyle;
import org.apache.poi.hssf.usermodel.HSSFDataFormat;
import org.apache.poi.hssf.usermodel.HSSFDataValidation;
import org.apache.poi.hssf.usermodel.HSSFFont;
import org.apache.poi.hssf.usermodel.HSSFRow;
import org.apache.poi.hssf.usermodel.HSSFSheet;
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
import org.apache.poi.ss.usermodel.CellType;
import org.apache.poi.ss.usermodel.HorizontalAlignment;
import org.apache.poi.ss.usermodel.VerticalAlignment;
import org.apache.poi.ss.util.CellRangeAddress;
import org.apache.poi.ss.util.CellRangeAddressList;
import org.jdom2.Attribute;
import org.jdom2.DataConversionException;
import org.jdom2.Document;
import org.jdom2.Element;
import org.jdom2.JDOMException;
import org.jdom2.input.SAXBuilder;

/**
 * 创建模板文件
 * 
 * @author AWG
 * @date 2020-02-15
 * @version 1.0
 */
public class CreateTemplate {

    /*
     * 创建log4j日志对象
     */
    private static final Logger logger = Logger.getLogger(CreateTemplate.class);

    public static void main(String[] args) {

        /*
         * 获取XML模板文件路径
         */
        String path = System.getProperty("user.dir") + "/excel-drdc/src/main/resources/student.xml";
        try {
            /*
             * 解析XML模板文件
             */
            SAXBuilder builder = new SAXBuilder();
            Document document = builder.build(new File(path));
            /*
             * 创建工作簿
             */
            HSSFWorkbook workbook = new HSSFWorkbook();
            /*
             * 创建工作表
             */
            HSSFSheet sheet = workbook.createSheet("Sheet0");
            /*
             * 获取XML文件的根节点
             */
            Element rootElement = document.getRootElement();
            /*
             * 根据根节点的属性获取模板的名称
             */
            String templateName = rootElement.getAttribute("name").getValue();
            /*
             * 设置列宽
             */
            setColumnWidth(sheet, rootElement);
            /*
             * 设置标题
             */
            setTitle(workbook, sheet, rootElement);
            /*
             * 设置表头
             */
            setSheetHead(workbook, sheet, rootElement);
            /*
             * 设置数据区域样式
             */
            setSheetDataAreaStyle(workbook, sheet, rootElement);
            /*
             * 生成Excel导入模板
             */
            File tempFile = new File(System.getProperty("user.home") + File.separatorChar + "Desktop" + File.separatorChar + templateName + ".xls");
            if (tempFile.exists()) {
                boolean result = tempFile.delete();
                logger.debug(result ? "模板文件删除成功" : "模板文件删除失败");
            } else {
                boolean result = tempFile.createNewFile();
                logger.debug(result ? "模板文件创建成功" : "模板文件创建失败");
            }
            FileOutputStream fos = FileUtils.openOutputStream(tempFile);
            workbook.write(fos);
            fos.close();
            workbook.close();
        } catch (JDOMException e) {
            // 文件解析失败
            //e.printStackTrace();
            logger.error(e.getMessage());
        } catch (IOException e) {
            // 文件读取失败
            //e.printStackTrace();
            logger.error(e.getMessage());
        }
    }

    /**
     * 设置数据区域样式
     * 
     * @param workbook    工作簿对象
     * @param sheet       工作表对象
     * @param rootElement 根节点对象
     * @throws DataConversionException 数据转换异常
     */
    private static void setSheetDataAreaStyle(HSSFWorkbook workbook, HSSFSheet sheet, Element rootElement) throws DataConversionException {
        // 初始化行数和列数。表格的第一行(索引0)为标题；表格的第二行(索引1)为表头；表格的第三行(索引2)开始为数据区域
        // 计算所有标题行和表头数量之和，其和即为数据区域的开始行索引
        int rowNum = rootElement.getChild("title").getChildren("tr").size() + rootElement.getChild("thead").getChildren("tr").size();
        // 获取"tr"节点
        Element trElement = rootElement.getChild("tbody").getChild("tr");
        // 获取"tr"节点的"repeat"属性值 ==> 默认初始化行数为5行
        int repeatValue = trElement.getAttribute("repeat").getIntValue();
        // 获取"td"节点组成的集合
        List<Element> tdElements = trElement.getChildren("td");
        // 遍历"td"节点组成的集合
        for (int i = 0; i < repeatValue; i++) {
            // 创建数据区域的行
            HSSFRow dataRow = sheet.createRow(rowNum);
            // 数据区域的行的行高
            dataRow.setHeightInPoints((short) 30);
            // 遍历"td"节点组成的集合来创建数据区域行的组成单元格
            for (int colNum = 0; colNum < tdElements.size(); colNum++) {
                // 创建数据区域行的组成单元格
                Element tdElement = tdElements.get(colNum);
                HSSFCell dataCell = dataRow.createCell(colNum);
                // 设置数据区域的数据的单元格样式
                setType(workbook, dataCell, tdElement);
            }
            rowNum++;
        }
    }

    /**
     * 设置数据区域的数据的单元格样式
     * 
     * @param workbook  工作簿
     * @param dataCell  单元格
     * @param tdElement 与单元格对应的XML模板文件节点元素
     */
    private static void setType(HSSFWorkbook workbook, HSSFCell dataCell, Element tdElement) {
        // 获取单元格的"type"属性 ==> 用于设置excel文件的数据区域单元格的数据的格式
        String typeValue = tdElement.getAttribute("type").getValue();
        // 数据格式化器
        HSSFDataFormat dataFormat = workbook.createDataFormat();
        // 单元格数据样式
        HSSFCellStyle cellStyle = workbook.createCellStyle();
        // 为每一种数据格式(string, numeric, enum, date, ...)进行设置
        if ("numeric".equalsIgnoreCase(typeValue)) {
            // 设置"numeric"单元格的数据类型
            dataCell.setCellType(CellType.NUMERIC);
            // dataCell.setCellType(HSSFCell.CELL_TYPE_NUMERIC);
            // 获取td节点的"format"的属性值
            Attribute formatAttribute = tdElement.getAttribute("format");
            String formatAttributeValue = formatAttribute.getValue();
            formatAttributeValue = StringUtils.isNotBlank(formatAttributeValue) ? formatAttributeValue : "#,##0.00";
            // 设置"numeric"单元格的数据格式
            cellStyle.setDataFormat(dataFormat.getFormat(formatAttributeValue));
        } else if ("string".equalsIgnoreCase(typeValue)) {
            dataCell.setCellValue("");
            dataCell.setCellType(CellType.STRING);
            // dataCell.getCellType(HSSFCell.CELL_TYPE_STRING);
            cellStyle.setDataFormat(dataFormat.getFormat("@"));
        } else if ("date".equalsIgnoreCase(typeValue)) {
            dataCell.setCellType(CellType.NUMERIC);
            // dataCell.setCellType(HSSFCell.CELL_TYPE_NUMERIC);
            cellStyle.setDataFormat(dataFormat.getFormat("yyyy-mm-dd"));
        } else if ("enum".equalsIgnoreCase(typeValue)) {
            // 加载下拉列表的内容
            String enumValue = tdElement.getAttribute("format").getValue();
            DVConstraint constraint = DVConstraint.createExplicitListConstraint(enumValue.split(","));
            // 数据有效性对象
            CellRangeAddressList regions = new CellRangeAddressList(dataCell.getRowIndex(), dataCell.getRowIndex(), dataCell.getColumnIndex(), dataCell.getColumnIndex());
            HSSFDataValidation dataValidation = new HSSFDataValidation(regions, constraint);
            workbook.getSheetAt(0).addValidationData(dataValidation);
        }
        // 为每个单元格的数据设置指定的样式
        dataCell.setCellStyle(cellStyle);
    }

    /**
     * 设置表头
     * 
     * @param workbook    工作簿对象
     * @param sheet       工作表对象
     * @param rootElement 根节点对象
     */
    private static void setSheetHead(HSSFWorkbook workbook, HSSFSheet sheet, Element rootElement) {
        HSSFCellStyle theadCellStyle = workbook.createCellStyle();
        HSSFFont theadFont = workbook.createFont();
        theadFont.setFontName("宋体");
        theadFont.setBold(true);
        theadFont.setFontHeightInPoints((short) 24);
        theadCellStyle.setFont(theadFont);
        theadCellStyle.setAlignment(HorizontalAlignment.CENTER);
        theadCellStyle.setVerticalAlignment(VerticalAlignment.CENTER);
        
        // 初始化行数和列数。表格的第一行(索引0)为标题；表格的第二行(索引1, 若没有副标题的情况之下)为表头
        int rowNum = rootElement.getChild("title").getChildren("tr").size();
         // 创建表头行（一定要先创建行，再在已创建行的基础上进行单元格（表头）的创建）
        HSSFRow headRow = sheet.createRow(rowNum);
        // 设置标题行的行高
        headRow.setHeightInPoints((short) 60);
        //获取"th"节点(集合),即真正的表头
        List<Element> thElements = rootElement.getChild("thead").getChild("tr").getChildren("th");
        // 遍历获取"th"节点(集合)
        for (int colNum = 0; colNum < thElements.size(); colNum++) {
            // 获取每一个"th"节点
            Element thElement = thElements.get(colNum);
            // 获取"th"节点的属性 ==> 表头的名称
            Attribute theadAttribute = thElement.getAttribute("value");
            // 创建"表头"单元格
            HSSFCell theadCell = headRow.createCell(colNum);
            if (theadAttribute != null) {
                theadCell.setCellValue(theadAttribute.getValue());
                theadCell.setCellStyle(theadCellStyle);
            }
        }
    }

    /**
     * 设置标题
     * 
     * @param workbook    工作簿对象
     * @param sheet       工作表对象
     * @param rootElement XML模板文件中根节点对象
     * @throws DataConversionException 数据转换异常
     */
    private static void setTitle(HSSFWorkbook workbook, HSSFSheet sheet, Element rootElement) throws DataConversionException {
        // 设置字体样式
        HSSFFont font = workbook.createFont();
        font.setFontName("宋体");
        font.setBold(true);
        font.setFontHeightInPoints((short) 36);
        font.setColor(HSSFFont.COLOR_RED);
        // 设置单元格文字对齐方式: 水平-垂直居中
        HSSFCellStyle cellStyle = workbook.createCellStyle();
        cellStyle.setAlignment(HorizontalAlignment.CENTER);
        cellStyle.setVerticalAlignment(VerticalAlignment.CENTER);
        cellStyle.setFont(font);
        // 获取"title(标题)"节点
        Element titleElement = rootElement.getChild("title");
        // 获取"title(标题)"节点的所有具体标题(即,可能有多个标题)
        List<Element> trElements = titleElement.getChildren("tr");
        // 遍历获取每一个"tr"节点
        for (int rowNum = 0; rowNum < trElements.size(); rowNum++) {
            // 获取每一个"tr"节点
            Element trElement = trElements.get(rowNum);
            // 获取每一个"tr"节点的子节点 ==> "td"
            Element tdElement = trElement.getChild("td");
            // 获取标题单元格的属性
            Attribute rowSpanAttribute = tdElement.getAttribute("rowspan");
            Attribute colSpanAttribute = tdElement.getAttribute("colspan");
            Attribute titleAttribute = tdElement.getAttribute("value");
            // 判空获取标题单元格的属性值
            if (titleAttribute != null) {
                // 获取并设置标题单元格的内容
                String titleAttributeValue = titleAttribute.getValue();
                // 创建标题单元格(一般来说，一个标题独占一行（跨多列）)
                HSSFCell titleCell = sheet.createRow(rowNum).createCell(0);
                titleCell.setCellValue(titleAttributeValue);
                titleCell.setCellStyle(cellStyle);
                // 获取标题单元格的位置索引
                int rowSpanAttributeValue = rowSpanAttribute.getIntValue() - 1;
                int colSpanAttributeValue = colSpanAttribute.getIntValue() - 1;
                // 合并单元格居中(索引从0开始.行数:第rowStart行~第rowEnd行; 列数:第colStart列~第colEnd列)
                sheet.addMergedRegion(new CellRangeAddress(rowSpanAttributeValue, rowSpanAttributeValue, 0, colSpanAttributeValue));
            }
        }
    }

    /**
     * 设置列宽
     * 
     * @param sheet       工作表对象
     * @param rootElement XML模板文件中根节点对象
     */
    private static void setColumnWidth(HSSFSheet sheet, Element rootElement) {
        // 子节点
        Element colgroupElement = rootElement.getChild("colgroup");
        // 获取XML模板文件中所有的"列"(组成一个集合)
        List<Element> cols = colgroupElement.getChildren("col");
        // 循环遍历为每一列设置宽度
        for (int i = 0; i < cols.size(); i++) {
            // 获取"每一列"代表的元素节点
            Element col = cols.get(i);
            // 获取"每一列"代表的元素节点的"width"属性
            Attribute widthAttribute = col.getAttribute("width");
            // 获取模板文件中"width"属性的属性值的单位(一般为em或px)
            String widthAttributeUnit = widthAttribute.getValue().replaceAll("[0-9.]", "");
            // 获取模板文件中"width"属性的属性值
            String widthAttributeValue = widthAttribute.getValue().replaceAll(widthAttributeUnit, "");
            // 初始化经过转化要写入到Excel中的宽度属性值(因为Java程序中的值与Excel中的值不对等)
            int calcValue = 0;
            if (StringUtils.isBlank(widthAttributeUnit) || "px".endsWith(widthAttributeUnit.toLowerCase())) {
                // 属性值的单位是"px"的计算方法
                calcValue = Math.round(Float.parseFloat(widthAttributeValue) * 37F);
            } else if ("em".endsWith(widthAttributeUnit.toLowerCase())) {
                // 属性值的单位是"em"的计算方法
                calcValue = Math.round(Float.parseFloat(widthAttributeValue) * 267.5F);
            }
            sheet.setColumnWidth(i, calcValue);
        }
    }

}
